package com.way.view;

import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;

import com.way.launcher.R;

/**
 * An abstraction of the original CellLayout which supports laying out items
 * which span multiple cells into a grid-like layout. Also supports dimming to
 * give a preview of its contents.
 * 代码来自4.0的Launcher
 * 
 * @author way
 */
public class PagedViewCellLayout extends ViewGroup implements Page {
	static final String TAG = "PagedViewCellLayout";

	private int mCellCountX;
	private int mCellCountY;
	private int mOriginalCellWidth;
	private int mOriginalCellHeight;
	private int mCellWidth;
	private int mCellHeight;
	private int mOriginalWidthGap;
	private int mOriginalHeightGap;
	private int mWidthGap;
	private int mHeightGap;
	private int mMaxGap;
	protected PagedViewCellLayoutChildren mChildren;

	public PagedViewCellLayout(Context context) {
		this(context, null);
	}

	public PagedViewCellLayout(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public PagedViewCellLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		setAlwaysDrawnWithCacheEnabled(false);

		// setup default cell parameters
		Resources resources = context.getResources();
		mOriginalCellWidth = mCellWidth = resources
				.getDimensionPixelSize(R.dimen.apps_customize_cell_width);
		mOriginalCellHeight = mCellHeight = resources
				.getDimensionPixelSize(R.dimen.apps_customize_cell_height);
		// mCellCountX = LauncherModel.getCellCountX();
		// mCellCountY = LauncherModel.getCellCountY();
		mOriginalWidthGap = mOriginalHeightGap = mWidthGap = mHeightGap = -1;
		mMaxGap = resources
				.getDimensionPixelSize(R.dimen.apps_customize_max_gap);

		mChildren = new PagedViewCellLayoutChildren(context);
		mChildren.setCellDimensions(mCellWidth, mCellHeight);
		mChildren.setGap(mWidthGap, mHeightGap);

		addView(mChildren);
	}

	public int getCellWidth() {
		return mCellWidth;
	}

	public int getCellHeight() {
		return mCellHeight;
	}

	@Override
	public void cancelLongPress() {
		super.cancelLongPress();

		// Cancel long press for all children
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			final View child = getChildAt(i);
			child.cancelLongPress();
		}
	}

	public boolean addViewToCellLayout(View child, int index, int childId,
			PagedViewCellLayout.LayoutParams params) {
		final PagedViewCellLayout.LayoutParams lp = params;

		// Generate an id for each view, this assumes we have at most 256x256
		// cells
		// per workspace screen
		if (lp.cellX >= 0 && lp.cellX <= (mCellCountX - 1) && lp.cellY >= 0
				&& (lp.cellY <= mCellCountY - 1)) {
			// If the horizontal or vertical span is set to -1, it is taken to
			// mean that it spans the extent of the CellLayout
			if (lp.cellHSpan < 0)
				lp.cellHSpan = mCellCountX;
			if (lp.cellVSpan < 0)
				lp.cellVSpan = mCellCountY;

			child.setId(childId);
			mChildren.addView(child, index, lp);

			return true;
		}
		return false;
	}

	@Override
	public void removeAllViewsOnPage() {
		mChildren.removeAllViews();
	}

	@Override
	public void removeViewOnPageAt(int index) {
		mChildren.removeViewAt(index);
	}

	/**
	 * Clears all the key listeners for the individual icons.
	 */
	public void resetChildrenOnKeyListeners() {
		int childCount = mChildren.getChildCount();
		for (int j = 0; j < childCount; ++j) {
			mChildren.getChildAt(j).setOnKeyListener(null);
		}
	}

	@Override
	public int getPageChildCount() {
		return mChildren.getChildCount();
	}

	public PagedViewCellLayoutChildren getChildrenLayout() {
		return mChildren;
	}

	@Override
	public View getChildOnPageAt(int i) {
		return mChildren.getChildAt(i);
	}

	@Override
	public int indexOfChildOnPage(View v) {
		return mChildren.indexOfChild(v);
	}

	public int getCellCountX() {
		return mCellCountX;
	}

	public int getCellCountY() {
		return mCellCountY;
	}

	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
		int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);

		int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
		int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

		if (widthSpecMode == MeasureSpec.UNSPECIFIED
				|| heightSpecMode == MeasureSpec.UNSPECIFIED) {
			throw new RuntimeException(
					"CellLayout cannot have UNSPECIFIED dimensions");
		}

		int numWidthGaps = mCellCountX - 1;
		int numHeightGaps = mCellCountY - 1;

		if (mOriginalWidthGap < 0 || mOriginalHeightGap < 0) {
			int hSpace = widthSpecSize - getPaddingLeft() - getPaddingRight();
			int vSpace = heightSpecSize - getPaddingTop() - getPaddingBottom();
			int hFreeSpace = hSpace - (mCellCountX * mOriginalCellWidth);
			int vFreeSpace = vSpace - (mCellCountY * mOriginalCellHeight);
			mWidthGap = Math.min(mMaxGap,
					numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0);
			mHeightGap = Math.min(mMaxGap,
					numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0);

			mChildren.setGap(mWidthGap, mHeightGap);
		} else {
			mWidthGap = mOriginalWidthGap;
			mHeightGap = mOriginalHeightGap;
		}

		// Initial values correspond to widthSpecMode == MeasureSpec.EXACTLY
		int newWidth = widthSpecSize;
		int newHeight = heightSpecSize;
		if (widthSpecMode == MeasureSpec.AT_MOST) {
			newWidth = getPaddingLeft() + getPaddingRight()
					+ (mCellCountX * mCellWidth)
					+ ((mCellCountX - 1) * mWidthGap);
			newHeight = getPaddingTop() + getPaddingBottom()
					+ (mCellCountY * mCellHeight)
					+ ((mCellCountY - 1) * mHeightGap);
			setMeasuredDimension(newWidth, newHeight);
		}

		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			View child = getChildAt(i);
			int childWidthMeasureSpec = MeasureSpec
					.makeMeasureSpec(newWidth - getPaddingLeft()
							- getPaddingRight(), MeasureSpec.EXACTLY);
			int childheightMeasureSpec = MeasureSpec
					.makeMeasureSpec(newHeight - getPaddingTop()
							- getPaddingBottom(), MeasureSpec.EXACTLY);
			child.measure(childWidthMeasureSpec, childheightMeasureSpec);
		}

		setMeasuredDimension(newWidth, newHeight);
	}

	int getContentWidth() {
		return getWidthBeforeFirstLayout() + getPaddingLeft()
				+ getPaddingRight();
	}

	int getContentHeight() {
		if (mCellCountY > 0) {
			return mCellCountY * mCellHeight + (mCellCountY - 1)
					* Math.max(0, mHeightGap);
		}
		return 0;
	}

	int getWidthBeforeFirstLayout() {
		if (mCellCountX > 0) {
			return mCellCountX * mCellWidth + (mCellCountX - 1)
					* Math.max(0, mWidthGap);
		}
		return 0;
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		int count = getChildCount();
		for (int i = 0; i < count; i++) {
			View child = getChildAt(i);
			child.layout(getPaddingLeft(), getPaddingTop(), r - l
					- getPaddingRight(), b - t - getPaddingBottom());
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		boolean result = super.onTouchEvent(event);
		int count = getPageChildCount();
		if (count > 0) {
			// We only intercept the touch if we are tapping in empty space
			// after the final row
			View child = getChildOnPageAt(count - 1);
			int bottom = child.getBottom();
			int numRows = (int) Math.ceil((float) getPageChildCount()
					/ getCellCountX());
			if (numRows < getCellCountY()) {
				// Add a little bit of buffer if there is room for another row
				bottom += mCellHeight / 2;
			}
			result = result || (event.getY() < bottom);
		}
		return result;
	}

	public void enableCenteredContent(boolean enabled) {
		mChildren.enableCenteredContent(enabled);
	}

	@Override
	protected void setChildrenDrawingCacheEnabled(boolean enabled) {
		mChildren.setChildrenDrawingCacheEnabled(enabled);
	}

	public void setCellCount(int xCount, int yCount) {
		mCellCountX = xCount;
		mCellCountY = yCount;
		requestLayout();
	}

	public void setGap(int widthGap, int heightGap) {
		mOriginalWidthGap = mWidthGap = widthGap;
		mOriginalHeightGap = mHeightGap = heightGap;
		mChildren.setGap(widthGap, heightGap);
	}

	public int[] getCellCountForDimensions(int width, int height) {
		// Always assume we're working with the smallest span to make sure we
		// reserve enough space in both orientations
		int smallerSize = Math.min(mCellWidth, mCellHeight);

		// Always round up to next largest cell
		int spanX = (width + smallerSize) / smallerSize;
		int spanY = (height + smallerSize) / smallerSize;

		return new int[] { spanX, spanY };
	}

	/**
	 * Start dragging the specified child
	 * 
	 * @param child
	 *            The child that is being dragged
	 */
	void onDragChild(View child) {
		PagedViewCellLayout.LayoutParams lp = (PagedViewCellLayout.LayoutParams) child
				.getLayoutParams();
		lp.isDragging = true;
	}

	/**
	 * Estimates the number of cells that the specified width would take up.
	 */
	public int estimateCellHSpan(int width) {
		// We don't show the next/previous pages any more, so we use the full
		// width, minus the
		// padding
		int availWidth = width - (getPaddingLeft() + getPaddingRight());

		// We know that we have to fit N cells with N-1 width gaps, so we just
		// juggle to solve for N
		int n = Math
				.max(1, (availWidth + mWidthGap) / (mCellWidth + mWidthGap));

		// We don't do anything fancy to determine if we squeeze another row in.
		return n;
	}

	/**
	 * Estimates the number of cells that the specified height would take up.
	 */
	public int estimateCellVSpan(int height) {
		// The space for a page is the height - top padding (current page) -
		// bottom padding (current
		// page)
		int availHeight = height - (getPaddingTop() + getPaddingBottom());

		// We know that we have to fit N cells with N-1 height gaps, so we
		// juggle to solve for N
		int n = Math.max(1, (availHeight + mHeightGap)
				/ (mCellHeight + mHeightGap));

		// We don't do anything fancy to determine if we squeeze another row in.
		return n;
	}

	/** Returns an estimated center position of the cell at the specified index */
	public int[] estimateCellPosition(int x, int y) {
		return new int[] {
				getPaddingLeft() + (x * mCellWidth) + (x * mWidthGap)
						+ (mCellWidth / 2),
				getPaddingTop() + (y * mCellHeight) + (y * mHeightGap)
						+ (mCellHeight / 2) };
	}

	public void calculateCellCount(int width, int height, int maxCellCountX,
			int maxCellCountY) {
		mCellCountX = Math.min(maxCellCountX, estimateCellHSpan(width));
		mCellCountY = Math.min(maxCellCountY, estimateCellVSpan(height));
		requestLayout();
	}

	/**
	 * Estimates the width that the number of hSpan cells will take up.
	 */
	public int estimateCellWidth(int hSpan) {
		// TODO: we need to take widthGap into effect
		return hSpan * mCellWidth;
	}

	/**
	 * Estimates the height that the number of vSpan cells will take up.
	 */
	public int estimateCellHeight(int vSpan) {
		// TODO: we need to take heightGap into effect
		return vSpan * mCellHeight;
	}

	@Override
	public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
		return new PagedViewCellLayout.LayoutParams(getContext(), attrs);
	}

	@Override
	protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
		return p instanceof PagedViewCellLayout.LayoutParams;
	}

	@Override
	protected ViewGroup.LayoutParams generateLayoutParams(
			ViewGroup.LayoutParams p) {
		return new PagedViewCellLayout.LayoutParams(p);
	}

	public static class LayoutParams extends ViewGroup.MarginLayoutParams {
		/**
		 * Horizontal location of the item in the grid.
		 */
		@ViewDebug.ExportedProperty
		public int cellX;

		/**
		 * Vertical location of the item in the grid.
		 */
		@ViewDebug.ExportedProperty
		public int cellY;

		/**
		 * Number of cells spanned horizontally by the item.
		 */
		@ViewDebug.ExportedProperty
		public int cellHSpan;

		/**
		 * Number of cells spanned vertically by the item.
		 */
		@ViewDebug.ExportedProperty
		public int cellVSpan;

		/**
		 * Is this item currently being dragged
		 */
		public boolean isDragging;

		// a data object that you can bind to this layout params
		private Object mTag;

		// X coordinate of the view in the layout.
		@ViewDebug.ExportedProperty
		int x;
		// Y coordinate of the view in the layout.
		@ViewDebug.ExportedProperty
		int y;

		public LayoutParams() {
			super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
			cellHSpan = 1;
			cellVSpan = 1;
		}

		public LayoutParams(Context c, AttributeSet attrs) {
			super(c, attrs);
			cellHSpan = 1;
			cellVSpan = 1;
		}

		public LayoutParams(ViewGroup.LayoutParams source) {
			super(source);
			cellHSpan = 1;
			cellVSpan = 1;
		}

		public LayoutParams(LayoutParams source) {
			super(source);
			this.cellX = source.cellX;
			this.cellY = source.cellY;
			this.cellHSpan = source.cellHSpan;
			this.cellVSpan = source.cellVSpan;
		}

		public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
			super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
			this.cellX = cellX;
			this.cellY = cellY;
			this.cellHSpan = cellHSpan;
			this.cellVSpan = cellVSpan;
		}

		public void setup(int cellWidth, int cellHeight, int widthGap,
				int heightGap, int hStartPadding, int vStartPadding) {

			final int myCellHSpan = cellHSpan;
			final int myCellVSpan = cellVSpan;
			final int myCellX = cellX;
			final int myCellY = cellY;

			width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap)
					- leftMargin - rightMargin;
			height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap)
					- topMargin - bottomMargin;

			x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
			y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
		}

		public Object getTag() {
			return mTag;
		}

		public void setTag(Object tag) {
			mTag = tag;
		}

		public String toString() {
			return "(" + this.cellX + ", " + this.cellY + ", " + this.cellHSpan
					+ ", " + this.cellVSpan + ")";
		}
	}
}

interface Page {
	public int getPageChildCount();

	public View getChildOnPageAt(int i);

	public void removeAllViewsOnPage();

	public void removeViewOnPageAt(int i);

	public int indexOfChildOnPage(View v);
}
